package com.twoscreen;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Text;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class EventServlet extends HttpServlet {
	private static final Logger log = Logger.getLogger("com.twoscreen.EventServlet.java");
	private static final long serialVersionUID = 1L;
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) {
		log.info("Event POST message received.");
		
		//Parameters passed in POST
		int code = 0;
		String viewerId = null;
		Boolean changeStatus = null;
		String videoId = null;
		if (request.getParameter("code") != null) {
			code = Integer.parseInt(request.getParameter("code"));
			log.info("code received: " + code);
		}
		if (request.getParameter("viewer") != null) {
			viewerId = request.getParameter("viewer");
			log.info("viewer received: " + viewerId);
		}
		if (request.getParameter("status") != null) {
			changeStatus = Boolean.valueOf(request.getParameter("status"));
			log.info("status received: " + changeStatus);
		}
		if (request.getParameter("videoId") != null) {
			videoId = request.getParameter("videoId");
			log.info("videoId received: " + videoId);
		}
		
		//Variables to parse JSON
		JSONObject json = null;
		JSONArray bodyArray = null;
		StringBuilder sb = new StringBuilder();
		BufferedReader reader = null;
		
		//Variables for event creation
		int time = -100;
		String action = null;
		String title = null;
		Boolean fullScreen = null;
		Text body = null;
		String vidTitle = null;
		String vidImg = null;
		
		//Get the user for validation
		UserService userService = UserServiceFactory.getUserService();
		User user = null;
		Boolean validUser = false;
		try {
			user = userService.getCurrentUser();
		} catch (Exception e) {
			log.warning("Error getting the user: " + e);
		}
		
		//Perform operation based on code
		switch (code) {
		case 1:
			//Submission of Event
			log.info("Event submitted");
			try {
		        reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
		        for (String line; (line = reader.readLine()) != null;) {
		            sb.append(line);
		            log.info("adding: " + line);
		        }
		    } catch (IOException e) {
		    	log.severe("IO error reading from input stream: " + e);
			} finally {
		        if (reader != null) try { reader.close(); } catch (IOException logOrIgnore) {}
		    }
		    try {
				json = new JSONObject(sb.toString());
			} catch (JSONException e1) {
				log.severe("Error parsing JSON object: " + e1);
			}
			
			Iterator<?> it = json.keys();
			while (it.hasNext()) {
				String key = (String) it.next();
				Object value = null;
				try {
					value = json.get(key);
				} catch (JSONException e) {
					log.severe("Error getting JSON object: " + e);
				}
				if (key.equals("videoId")) {
					videoId = value.toString();
				} else if (key.equals("viewerId")) {
					viewerId = value.toString();
				} else if (key.equals("time")) {
					time = Integer.parseInt(value.toString());
				} else if (key.equals("title")) {
					title = value.toString();
				} else if (key.equals("youtubeVideoTitle")) {
					vidTitle = value.toString();
				} else if (key.equals("youtubeVideoImg")) {
					vidImg = value.toString();
				} else if (key.equals("fullscreen")) {
					fullScreen = Boolean.parseBoolean(value.toString());
				} else if (key.equals("bodyElements")) {
					try {
						bodyArray = new JSONArray(value.toString());
					} catch (JSONException e) {
						log.severe("Error parsing JSON array: " + e);
					}
				}
			}
			
			//Once all data is collected:
			
			//process body elements
			StringBuilder bodySB = new StringBuilder();
			try {
				for (int i=0 ; i<bodyArray.length() ; i++) {
					JSONObject element = (JSONObject) bodyArray.get(i);
					String type = (String) element.get("type");
					JSONObject properties = element.getJSONObject("properties");
					bodySB.append(getBody(videoId, type, properties));
				}
			} catch (JSONException e) {
				log.severe("Error parsing JSON array: " + e);
			}
			body = new Text(bodySB.toString());
			//Set action number
			if (time == -1) {
				action = "#home";
			} else {
				List<Event> event = Event.getEvents(videoId);
				int size = event.size();
				action = "#event" + size;
			}
			
			try {
				validUser = Validator.checkGoogleUserViewerID(user, viewerId);
			} catch (Exception e) {
				log.warning("Error getting the user: " + e);
			}
			
			
			//If everything is ok and the user is valid save the event
			if (videoId != null && viewerId != null && time != -100 && action != null && title != null && body != null && fullScreen != null
					&& vidTitle != null && vidImg != null && validUser) {
				//Submission of event
				new Event(videoId, viewerId, time, action, title, body, fullScreen, vidTitle, vidImg);
				String rtn = "{\"status\": \"ok\"}";
				response.setContentType("application/json, charset=UTF-8");
				try {
					response.getWriter().println(rtn);
				} catch (IOException e) {
					log.warning("JSON response error: " + e);
				}
				log.info("Event saved");
			} else {
				log.warning("Event post contained incomplete/unrecognised data" + 
						" videoID: " + videoId + 
						" viewerId: " + viewerId + 
						" time: " + time + 
						" action: " + action + 
						" title: " + title + 
						" body: " + body + 
						" fullScreen: " + fullScreen + 
						" vidTitle: " + vidTitle + 
						" vidImg: " + vidImg + 
						" validUser: " + validUser);
				String rtn = "{\"status\": \"error\"}";
				response.setContentType("application/json, charset=UTF-8");
				try {
					response.getWriter().println(rtn);
				} catch (IOException e) {
					log.warning("JSON response error: " + e);
				}
			}
			break;
			
		case 2:
			//Change of Video status
			log.info("Video change submitted");
			try {
				validUser = Validator.checkGoogleUserViewerID(user, viewerId);
			} catch (Exception e) {
				log.warning("Error getting the user: " + e);
			}
			if (validUser) {
				log.info("setting status for video id: " + videoId + " to: " + changeStatus);
				Video.setEnabled(videoId, changeStatus);
				log.info("set status to: " + changeStatus);
			} else {
				log.warning("Failed to validate user");
			}
			break;
		
		default:
			log.warning("no valid code received");
			break;
		
		}
		
	}
	
	protected void doGet(HttpServletRequest request, HttpServletResponse response) {
		log.info("Event GET message received.");
		int code = 0;
		String videoId = null;
		String key = null;
		String viewerId = null;
		
		if (request.getParameter("code") != null) {
			code = Integer.parseInt(request.getParameter("code"));
			log.info("code received: " + code);
		}
		if (request.getParameter("vidId") != null) {
			videoId = request.getParameter("vidId");
			log.info("videoId received: " + videoId);
		}
		if (request.getParameter("key") != null) {
			key = request.getParameter("key");
			log.info("key received: " + key);
		}
		if (request.getParameter("viewer") != null) {
			viewerId = request.getParameter("viewer");
			log.info("viewer received: " + viewerId);
		}
		
		//Get the user for validation
		UserService userService = UserServiceFactory.getUserService();
		User user = null;
		Boolean validUser = false;
		try {
			user = userService.getCurrentUser();
			validUser = Validator.checkGoogleUserViewerID(user, viewerId);
		} catch (Exception e) {
			log.warning("Error getting the user: " + e);
		}
			
		if (validUser) {
			switch (code) {
			
			case 1:
				//Request for home text - home is stored at -1 seconds
				log.info("Home text requested");
				String home = null;
				Key k1 = KeyFactory.createKey(Event.class.getSimpleName(), videoId+"-1");
				home = Event.getBodyText(k1);
				log.info("home text: " + home);
				response.setContentType("text/html");
				try {
					response.getWriter().println(home);
					log.info("Home text sent");
				} catch (IOException e) {
					log.warning("Home text response error: " + e);
				}
				break;
				
			case 2:
				//Request for current events JSON
				log.info("Current events JSON requested");
				JSONArray rtn = Event.getEventsJson(videoId);
				response.setContentType("application/json, charset=UTF-8");
				try {
					response.getWriter().println(rtn);
					log.info("Current events JSON sent");
				} catch (IOException e) {
					log.warning("Current events JSON response error: " + e);
				}
				break;
				
			case 3:
				//Request for JSON array of user's videos
				log.info("Users videos requested");
				String rtn1 = Video.getEnabledVideosJson(viewerId);
				response.setContentType("application/json, charset=UTF-8");
				try {
					response.getWriter().println(rtn1);
					log.info("Users videos JSON sent");
				} catch (IOException e) {
					log.warning("Users videos JSON response error: " + e);
				}
				break;
				
			default:
				log.warning("Invalid request code received: " + code);
				break;
			
			}
		}
		
	}
	
	protected void doDelete(HttpServletRequest request, HttpServletResponse response) {
		log.info("Event DELETE message received.");
		int code = 0;
		String key = null;
		Key eventKey = null;
		
		if (request.getParameter("code") != null) {
			code = Integer.parseInt(request.getParameter("code"));
			log.info("code received: " + code);
		}
		if (request.getParameter("key") != null) {
			key = request.getParameter("key");
			eventKey = KeyFactory.createKey(Event.class.getSimpleName(), key);
			log.info("key received: " + key);
		}
		
		//Get the user for validation
		UserService userService = UserServiceFactory.getUserService();
		User user = null;
		Boolean validUser = false;
		String vidId = Event.getEvent(eventKey).getVideoId();
		try {
			user = userService.getCurrentUser();
			validUser = Validator.checkGoogleUserViewerID(user, Video.getVideo(vidId).getViewerId());
		} catch (Exception e) {
			log.warning("Error getting the user: " + e);
		}
		
		//Only delete the event if the user is validated
		if (validUser) {
			switch (code) {
			case 4:
				//Request for event to be deleted
				log.info("Event deletion requested");
				Event.deleteEvent(eventKey);
				break;
			}
		}
		
	}
	
	/**
	 * Processes each element of the body to return HTML code
	 * @param videoId Video ID for the code to generate
	 * @param type Type of code to generate
	 * @param properties JSONObject of the properties from the data store
	 * @return HTML code in a String
	 */
	private static String getBody(String videoId, String type, JSONObject properties) {
		//Processes each element type of the "bodyElements" array
		String body = null;
		long qid = 0;
		int points = -1;
		
		//Determine the type and create the body HTML
		if (type.equals("app-multi-choice")) {
			String question = null;
			String optionA = null;
			String optionB = null;
			String optionC = null;
			String optionD = null;
			String correctAnswer = null;
			try {
				question = properties.getString("question");
				optionA = properties.getString("optionA");
				optionB = properties.getString("optionB");
				correctAnswer = properties.getString("correctAnswer");
				points = properties.getInt("points");
				optionC = properties.getString("optionC");
				optionD = properties.getString("optionD");
			} catch (Exception e) {
				log.severe("Error getting properties: " + e);
			}
			
			//Clear options C and/or D if they are blank
			if (optionC.equals("")) {
				optionC = null;
			}
			if (optionD.equals("")) {
				optionD = null;
			}
			
			log.info("Received: " + 
					" Question: " + question +
					" optionA: " + optionA +
					" optionB: " + optionB +
					" correctAnswer: " + correctAnswer +
					" points: " + points +
					" optionC: " + optionC +
					" optionD: " + optionD);
			if (question != null && optionA != null && optionB != null && correctAnswer != null 
					&& videoId != null && points != -1) {
				QuestionMulti submitted = new QuestionMulti(question, optionA, optionB, optionC, optionD, 
						correctAnswer, videoId, points);
				log.info("Multiple choice question saved");
				//Get the question id
				qid = submitted.getKey().getId();
			}
			if (optionC != null && optionD != null) {
				log.info("MultiQuestion with 4 options");
				body = "<p>" + question + "</p>" +
						"<a href=\"#\" onclick=\"responseSubmit('" + optionA + "', " + qid + ", viewer, '#respTo" + qid + 
							"');\" data-role=\"button\">" + optionA + "</a>" +
						"<a href=\"#\" onclick=\"responseSubmit('" + optionB + "', " + qid + ", viewer, '#respTo" + qid + 
							"');\" data-role=\"button\">" + optionB + "</a>" +
						"<a href=\"#\" onclick=\"responseSubmit('" + optionC + "', " + qid + ", viewer, '#respTo" + qid + 
							"');\" data-role=\"button\">" + optionC + "</a>" +
						"<a href=\"#\" onclick=\"responseSubmit('" + optionD + "', " + qid + ", viewer, '#respTo" + qid + 
							"');\" data-role=\"button\">" + optionD + "</a>";
			} else if (optionC != null) {
				log.info("MultiQuestion with 3 options");
				body = "<p>" + question + "</p>" +
						"<a href=\"#\" onclick=\"responseSubmit('" + optionA + "', " + qid + ", viewer, '#respTo" + qid + 
							"');\" data-role=\"button\">" + optionA + "</a>" +
						"<a href=\"#\" onclick=\"responseSubmit('" + optionB + "', " + qid + ", viewer, '#respTo" + qid + 
							"');\" data-role=\"button\">" + optionB + "</a>" +
						"<a href=\"#\" onclick=\"responseSubmit('" + optionC + "', " + qid + ", viewer, '#respTo" + qid + 
							"');\" data-role=\"button\">" + optionC + "</a>";
			} else {
				log.info("MultiQuestion with 2 options");
				body = "<p>" + question + "</p>" +
						"<a href=\"#\" onclick=\"responseSubmit('" + optionA + "', " + qid + ", viewer, '#respTo" + qid + 
							"');\" data-role=\"button\">" + optionA + "</a>" +
						"<a href=\"#\" onclick=\"responseSubmit('" + optionB + "', " + qid + ", viewer, '#respTo" + qid + 
							"');\" data-role=\"button\">" + optionB + "</a>";
			}
			
			
		} else if (type.equals("app-heading-three")) {
			String text = null;
			try {
				text = properties.getString("text");
			} catch (Exception e) {
				log.severe("Error getting properties: " + e);
			}
			body = "<h3>" + text + "</h3>";
			
		} else if (type.equals("app-paragraph")) {
			String text = null;
			try {
				text = properties.getString("text");
			} catch (Exception e) {
				log.severe("Error getting properties: " + e);
			}
			body = "<p>" + text + "</p>";
			
		} else if (type.equals("app-image")) {
			String url = null;
			try {
				url = properties.getString("url");
			} catch (Exception e) {
				log.severe("Error getting properties: " + e);
			}
			body = "<img class=\"fullWidthRotate\" src=\"" + url + "\" />";
			
		} else if (type.equals("app-tube-status")) {
			body = "<iframe class=\"fullWidthRotate\" id=\"tfl\"" +
					"src=\"http://www.tfl.gov.uk/tfl/syndication/feeds/serviceboard-fullscreen.htm\" scrolling=\"no\"></iframe>";
			
		} else if (type.equals("app-google-plus")) {
			String url = null;
			try {
				url = properties.getString("url");
			} catch (Exception e) {
				log.severe("Error getting properties: " + e);
			}
			body = "<div class=\"g-plus\" data-href=\"" + url + "\" data-rel=\"publisher\"></div>";
			
		} else if (type.equals("app-facebook-like")) {
			String url = null;
			try {
				url = properties.getString("url");
			} catch (Exception e) {
				log.severe("Error getting properties: " + e);
			}
			body = "<iframe src=\"//www.facebook.com/plugins/like.php?href=" + url + "&amp;" +
					"send=false&amp;layout=standard&amp;width=450&amp;show_faces=true&amp;action=like&amp;" +
					"colorscheme=light&amp;font&amp;height=80\" scrolling=\"no\" frameborder=\"0\" " +
					"style=\"border:none; overflow:hidden; width:450px; height:80px;\" allowTransparency=\"true\"></iframe>";
			
		} else if (type.equals("app-twitter-hash")) {
			String tag = null;
			try {
				tag = properties.getString("tag");
			} catch (Exception e) {
				log.severe("Error getting properties: " + e);
			}
			body = "<a href=\"https://twitter.com/intent/tweet?button_hashtag=" + tag + "\" class=\"twitter-hashtag-button\" " +
					"data-size=\"large\" data-url=\"http://two-screen.appspot.com\">Tweet #" + tag + "</a>" +
					"<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);" +
					"js.id=id;js.src=\"//platform.twitter.com/widgets.js\";fjs.parentNode.insertBefore(js,fjs);}}(document,\"script\",\"twitter-wjs\");</script>";
			
		} else if (type.equals("app-home")) {
			String img = null;
			String text = null;
			try {
				img = properties.getString("img");
				text = properties.getString("text");
			} catch (Exception e) {
				log.severe("Error getting properties: " + e);
			}
			body = "<div id=\"app-home\" class=\"center-wrapper\">" +
					"<img id=\"app-home-img\" width=\"80%\" src=\"" + img + "\" alt=\"home logo\"/>" + 
					"<p id=\"app-home-text\">" + text + "</p>" +
					"</div>";
		}
		
		Iterator<?> it = properties.keys();
		while (it.hasNext()) {
			String key = (String) it.next();
			Object value = null;
			try {
				value = properties.get(key).toString();
				System.out.println("property key: " + key);
				System.out.println("property value: " + value);
			} catch (JSONException e) {
				log.severe("Error getting JSON object: " + e);
			}
		}
		
		return body;
	}
	

	
}
